import 'package:json_annotation/json_annotation.dart';

part 'virtual_file_node.g.dart';

enum NodeType {
  @JsonValue('folder')
  folder,
  @JsonValue('file')
  file,
}

@JsonSerializable()
class VirtualFileNode {
  final int? id;
  final String name;
  final NodeType type;
  final int? parentId;
  final String? fileMd5;
  final String path;
  final DateTime createdTime;
  final DateTime updatedTime;
  final bool isDeleted;

  VirtualFileNode({
    this.id,
    required this.name,
    required this.type,
    this.parentId,
    this.fileMd5,
    required this.path,
    required this.createdTime,
    required this.updatedTime,
    this.isDeleted = false,
  });

  factory VirtualFileNode.fromJson(Map<String, dynamic> json) =>
      _$VirtualFileNodeFromJson(json);

  Map<String, dynamic> toJson() => _$VirtualFileNodeToJson(this);

  factory VirtualFileNode.fromMap(Map<String, dynamic> map) {
    return VirtualFileNode(
      id: map['id']?.toInt(),
      name: map['name'] ?? '',
      type: NodeType.values.firstWhere(
        (e) => e.toString().split('.').last == map['type'],
        orElse: () => NodeType.file,
      ),
      parentId: map['parent_id']?.toInt(),
      fileMd5: map['file_md5'],
      path: map['path'] ?? '',
      createdTime: DateTime.parse(map['created_time'] ?? DateTime.now().toIso8601String()),
      updatedTime: DateTime.parse(map['updated_time'] ?? DateTime.now().toIso8601String()),
      isDeleted: map['is_deleted'] == 1,
    );
  }

  Map<String, dynamic> toMap() {
    return {
      'id': id,
      'name': name,
      'type': type.toString().split('.').last,
      'parent_id': parentId,
      'file_md5': fileMd5,
      'path': path,
      'created_time': createdTime.toIso8601String(),
      'updated_time': updatedTime.toIso8601String(),
      'is_deleted': isDeleted ? 1 : 0,
    };
  }

  VirtualFileNode copyWith({
    int? id,
    String? name,
    NodeType? type,
    int? parentId,
    String? fileMd5,
    String? path,
    DateTime? createdTime,
    DateTime? updatedTime,
    bool? isDeleted,
  }) {
    return VirtualFileNode(
      id: id ?? this.id,
      name: name ?? this.name,
      type: type ?? this.type,
      parentId: parentId ?? this.parentId,
      fileMd5: fileMd5 ?? this.fileMd5,
      path: path ?? this.path,
      createdTime: createdTime ?? this.createdTime,
      updatedTime: updatedTime ?? this.updatedTime,
      isDeleted: isDeleted ?? this.isDeleted,
    );
  }

  bool get isFolder => type == NodeType.folder;
  bool get isFile => type == NodeType.file;

  @override
  String toString() {
    return 'VirtualFileNode(id: $id, name: $name, type: $type, parentId: $parentId, fileMd5: $fileMd5, path: $path, createdTime: $createdTime, updatedTime: $updatedTime, isDeleted: $isDeleted)';
  }

  @override
  bool operator ==(Object other) {
    if (identical(this, other)) return true;
    return other is VirtualFileNode &&
        other.id == id &&
        other.name == name &&
        other.type == type &&
        other.parentId == parentId &&
        other.fileMd5 == fileMd5 &&
        other.path == path &&
        other.createdTime == createdTime &&
        other.updatedTime == updatedTime &&
        other.isDeleted == isDeleted;
  }

  @override
  int get hashCode {
    return id.hashCode ^
        name.hashCode ^
        type.hashCode ^
        parentId.hashCode ^
        fileMd5.hashCode ^
        path.hashCode ^
        createdTime.hashCode ^
        updatedTime.hashCode ^
        isDeleted.hashCode;
  }
}